<?php
namespace Btr\DurakBundle\Durak;

class Player extends \ArrayObject
{
	/**
	 * The cards an player holds, it contains values like 0x1A, 0x4B, 0x29,...
	 * @var array
	 */
	protected  $cards;
	
	/**
	 * The cards an player can attack.
	 * @var array
	 */
	protected $cardsAttackable;
	
	/**
	 * The cards an player can defend
	 * @var array
	 */
	protected $cardsDefendable;
	
	
	public function __construct(Array $cards = array())
	{
		$this->cards = $cards;
		if ( $cards )
		{
			parent::__construct($cards);
		}
	}

	/**
	 * Place cards by its value.
	 * @return array
	 */
	public function getCardsByValue()
	{
		return $this->organizeCardsByValue($this->cards);
	}
	
	/**
	 * Place cards by its suit.
	 * @return array
	 */
	public function getCardsBySuit()
	{
		return $this->organizeCardsBySuit($this->cards);
	}
	
	/**
	 * Place cards attackable by its value.
	 * @return array
	 */
	public function getCardsAttackableByValue()
	{
		return $this->organizeCardsByValue($this->cardsAttackable);		
	}

	/**
	 * Place cards attackable by its suit.
	 * @return array
	 */
	public function getCardsAttackableBySuit()
	{
		return $this->organizeCardsBySuit($this->cardsAttackable);		
	}
	
	/**
	 * Place cards defendable by its value.
	 * @return array
	 */
	public function getCardsDefendableByValue()
	{
		return $this->organizeCardsByValue($this->cardsDefendable);
	}
	
	/**
	 * Place cards defendable by its suit
	 * @return array
	 */
	public function getCardsDefendableBySuit()
	{
		return $this->organizeCardsBySuit($this->cardsDefendable);
	}
	
	/**
	 * Get cards which can attack.
	 * @param Durak $game
	 * @return array
	 */
	public function getCardsAttackable(Durak $game)
	{
		$cardsAttackable = array();
		// master suit
		$masterSuit = $game->getCards()->getMasterSuit() & 0xF0;
		
		// if attacked before, have to check cards attacked
		$cardsAvailable = $game->getCards();
		$rounds = $game->getRounds();
		// The cards we will check 
		$cardsRounded = array();		
		if (count($rounds))
		{
			foreach ( $rounds as $attackCard => $defenderCard)
			{
				$cardsRounded[ $attackCard & 0x0F ] = true;
			}
			$lastDefenderCard = end($rounds);
			$cardsRounded[ $lastDefenderCard & 0x0F ] = true;
			$cards = $this->getCardsByValue();
				
			// find all cards' values which are attackable.
			foreach ( $cards as $value => $suits )
			{
				if (array_key_exists($value, $cardsRounded))
				{
					foreach ( $suits as $suit ) $cardsAttackable[] = $value + $suit;
				}
			}
		}
		else
		{
			$cardsAttackable = $this->cards;
		}
		$this->cardsAttackable = $cardsAttackable;
		return $cardsAttackable;
	}
	
	/**
	 * Get all cards which can be defendable.
	 * @param Durak $game
	 * @param integer $attackCard
	 * @return array
	 */
	public function getCardsDefendable(Durak $game, $attackCard)
	{
		$cardsDefendable = array();
		// master suit
		$masterSuit = $game->getCards()->getMasterSuit() & 0xF0;
		$attackSuit = $attackCard & 0xF0;
		$attackValue = $attackCard & 0x0F;
		
		$cardsBySuit = $this->getCardsBySuit();
		if ( $attackSuit != $masterSuit )
		{
			// Get all card which has same suit and is greater than attack card.
			if (array_key_exists($attackSuit, $cardsBySuit))
			{
				foreach ($cardsBySuit[$attackSuit] as $card)
				{
					if ( $card > $attackValue ) $cardsDefendable[] = $attackSuit + $card;
				}
			}
			// Get all master suit card.
			if ( array_key_exists($masterSuit, $cardsBySuit))
			{
				foreach ($cardsBySuit[$masterSuit] as $card) $cardsDefendable[] = $masterSuit + $card;				
			}
		}
		else 
		{  // Get all master suit cards which is greater than attack card.
			if ( array_key_exists($masterSuit, $cardsBySuit))
			{
				foreach ($cardsBySuit[$masterSuit] as $card) 
				{
					if ( $card > $attackValue ) $cardsDefendable[] = $masterSuit + $card;
				}
			}
		}
		$this->cardsDefendable = $cardsDefendable;
		return $cardsDefendable;
	}
	
	/**
	 * Get card to attack.
	 * @param integer $masterSuit
	 * @param array $rounds
	 * @return boolean
	 */
	public function getAttackCard(Durak $game)
	{
		// Attacker needs more cards.
		if (count($this->cards) == 0 ) return 0;
		
		// Defender needs more cards.
		$defender = $game->getDefender();
		if ( count($defender->getCards()) == 0 ) return 0;
		
		// Can not attack automatically
		return -2;
	}
	
	/**
	 * Do not support for human.
	 * @param Durak $game
	 * @param integer $attackCard
	 * @return number
	 */
	public function getDefendCard(Durak $game, $attackCard)
	{
		// Can not defend automatically
		return -2;
	}
	
	
	/**
	 * A card was attacked, remove it from player.
	 * @param integer $card
	 */
	public function attack( $card )
	{
		$this->removeCard($card);
	}
	
	/**
	 * A card was defended, remove it from player.
	 * @param integer $card
	 */
	public function defend($card)
	{
		$this->removeCard($card);
	}
	
	/**
	 * Does it support accept automatically ?
	 * @return boolean
	 */
	public function isAutoAccept()
	{
		return false;
	}
	
	/**
	 * Place cards by its value.
	 * @param array $cards
	 * @return array
	 */
	private function organizeCardsByValue(array $cards)
	{
		$result = array();
		foreach ($cards as $card)
		{
			$result[$card & 0x0F][] = $card & 0xF0;
		}
		ksort($result);
		foreach ($result as &$cards) sort($cards);
		
		return $result;
	}
	
	/**
	 * Place cards by its suit.
	 * @param array $cards
	 * @return array
	 */
	private function organizeCardsBySuit(array $cards)
	{
		$result = array();
		foreach ($cards as $card)
		{
			$result[$card & 0xF0][] = $card & 0x0F;
		}
		ksort($result);
		foreach ($result as &$cards) sort($cards);
		return $result;
	}
	
	/**
	 * Remove a card from holder.
	 * @param integer $card
	 */
	private function removeCard($card)
	{
		$key = array_search($card, $this->cards);
		if ( $key === false ) return;
		array_splice($this->cards, $key,1);
	}
	
	/**
	 * Add cards.
	 * @param array $cards
	 */
	public function addCards(array $cards)
	{
	   $this->cards = array_merge($this->cards, $cards);	
	}
	/**
	 *
	 * @return the array
	 */
	public function getCards() 
	{
		return $this->cards;
	}
	
}
